[XEN] Restore backwards compatibility by supporting __xen_guest
authorIan Campbell <ian.campbell@xensource.com>
Wed, 23 Aug 2006 17:38:49 +0000 (18:38 +0100)
committerIan Campbell <ian.campbell@xensource.com>
Wed, 23 Aug 2006 17:38:49 +0000 (18:38 +0100)
section in dom0 loader.

Signed-off-by: Ian Campbell <ian.campbell@xensource.com>
xen/arch/x86/domain_build.c
xen/common/elf.c
xen/include/xen/sched.h

index 9fecc74ed95b1c54cadcf2bdae65283d6607af0b..adb903762edea64b38cc1694e3b6ecb48699c21b 100644 (file)
@@ -290,14 +290,7 @@ int construct_dom0(struct domain *d,
     if ( (rc = parseelfimage(&dsi)) != 0 )
         return rc;
 
-    if ( dsi.__elfnote_section == NULL )
-    {
-        printk("Not a Xen-ELF image: no Xen ELF notes were found.\n");
-        return -EINVAL;
-    }
-
-    p = xen_elfnote_string(&dsi, XEN_ELFNOTE_PAE_MODE);
-    dom0_pae = !!(p != NULL && strcmp(p, "yes") == 0);
+    dom0_pae = (dsi.pae_kernel != PAEKERN_no);
     xen_pae  = (CONFIG_PAGING_LEVELS == 3);
     if ( dom0_pae != xen_pae )
     {
@@ -306,8 +299,8 @@ int construct_dom0(struct domain *d,
         return -EINVAL;
     }
 
-    if ( xen_pae )
-        set_bit(VMASST_TYPE_pae_extended_cr3, &d->vm_assist);
+    if ( xen_pae && dsi.pae_kernel == PAEKERN_extended_cr3 )
+            set_bit(VMASST_TYPE_pae_extended_cr3, &d->vm_assist);
 
     if ( (p = xen_elfnote_string(&dsi, XEN_ELFNOTE_FEATURES)) != NULL )
     {
index 8cdeb0e737225471cc7d09b4a9cb4862d5f3ebce..594060c99b2e9f3131b9510c293334f29efa2822 100644 (file)
@@ -22,6 +22,80 @@ static inline int is_loadable_phdr(Elf_Phdr *phdr)
             ((phdr->p_flags & (PF_W|PF_X)) != 0));
 }
 
+/*
+ * Fallback for kernels containing only the legacy __xen_guest string
+ * and no ELF notes.
+ */
+static int is_xen_guest_section(Elf_Shdr *shdr, const char *shstrtab)
+{
+    return strcmp(&shstrtab[shdr->sh_name], "__xen_guest") == 0;
+}
+
+static const char *xen_guest_lookup(struct domain_setup_info *dsi, int type)
+{
+    const char *xenguest_fallbacks[] = {
+        [XEN_ELFNOTE_ENTRY] = "VIRT_ENTRY=",
+        [XEN_ELFNOTE_HYPERCALL_PAGE] = "HYPERCALL_PAGE=",
+        [XEN_ELFNOTE_VIRT_BASE] = "VIRT_BASE=",
+        [XEN_ELFNOTE_PADDR_OFFSET] = "ELF_PADDR_OFFSET=",
+        [XEN_ELFNOTE_XEN_VERSION] = "XEN_VER=",
+        [XEN_ELFNOTE_GUEST_OS] = "GUEST_OS=",
+        [XEN_ELFNOTE_GUEST_VERSION] = "GUEST_VER=",
+        [XEN_ELFNOTE_LOADER] = "LOADER=",
+        [XEN_ELFNOTE_PAE_MODE] = "PAE=",
+        [XEN_ELFNOTE_FEATURES] = "FEATURES=",
+        [XEN_ELFNOTE_BSD_SYMTAB] = "BSD_SYMTAB=",
+    };
+    const char *fallback;
+    const char *p;
+
+    if ( type > sizeof(xenguest_fallbacks) )
+        return NULL;
+
+    if ( (fallback = xenguest_fallbacks[type]) == NULL )
+        return NULL;
+
+    if ( (p = strstr(dsi->__xen_guest_string,fallback)) == NULL )
+        return NULL;
+
+    return p + strlen(fallback);
+}
+
+static const char *xen_guest_string(struct domain_setup_info *dsi, int type)
+{
+    const char *p = xen_guest_lookup(dsi, type);
+
+    /*
+     * We special case this since the __xen_guest_section treats the
+     * mere precense of the BSD_SYMTAB string as true or false.
+     */
+    if ( type == XEN_ELFNOTE_BSD_SYMTAB )
+        return p ? "yes" : "no";
+
+    return p;
+}
+
+static unsigned long long xen_guest_numeric(struct domain_setup_info *dsi,
+                                                   int type, int *defined)
+{
+    const char *p = xen_guest_lookup(dsi, type);
+    unsigned long long value;
+
+    if ( p == NULL )
+        return 0;
+
+    value = simple_strtoull(p, NULL, 0);
+
+    /* We special case this since __xen_guest_section contains a PFN
+     * for this field not a virtual address.
+     */
+    if (type == XEN_ELFNOTE_HYPERCALL_PAGE)
+        value = dsi->v_start + (value<<PAGE_SHIFT);
+
+    *defined = 1;
+    return value;
+}
+
 /*
  * Interface to the Xen ELF notes.
  */
@@ -65,7 +139,6 @@ static Elf_Note *xen_elfnote_lookup(struct domain_setup_info *dsi, int type)
             return note;
     }
 
-    DPRINTK("unable to find Xen ELF note with type %#x\n", type);
     return NULL;
 }
 
@@ -73,13 +146,13 @@ const char *xen_elfnote_string(struct domain_setup_info *dsi, int type)
 {
     Elf_Note *note;
 
+    if ( !dsi->__elfnote_section )
+        return xen_guest_string(dsi, type);
+
     note = xen_elfnote_lookup(dsi, type);
     if ( note == NULL )
         return NULL;
 
-    DPRINTK("found Xen ELF note type %#x = \"%s\"\n",
-            type, (char *)ELFNOTE_DESC(note));
-
     return (const char *)ELFNOTE_DESC(note);
 }
 
@@ -90,6 +163,9 @@ unsigned long long xen_elfnote_numeric(struct domain_setup_info *dsi,
 
     *defined = 0;
 
+    if ( !dsi->__elfnote_section )
+        return xen_guest_numeric(dsi, type, defined);
+
     note = xen_elfnote_lookup(dsi, type);
     if ( note == NULL )
     {
@@ -105,6 +181,8 @@ unsigned long long xen_elfnote_numeric(struct domain_setup_info *dsi,
         *defined = 1;
         return *(uint64_t*)ELFNOTE_DESC(note);
     default:
+        printk("ERROR: unknown data size %#x for numeric type note %#x\n",
+               note->descsz, type);
         return 0;
     }
 }
@@ -146,6 +224,7 @@ int parseelfimage(struct domain_setup_info *dsi)
     shstrtab = image + shdr->sh_offset;
 
     dsi->__elfnote_section = NULL;
+    dsi->__xen_guest_string = NULL;
 
     /* Look for .notes segment containing at least one Xen note */
     for ( h = 0; h < ehdr->e_shnum; h++ )
@@ -159,27 +238,73 @@ int parseelfimage(struct domain_setup_info *dsi)
         break;
     }
 
-    /* Check the contents of the Xen notes. */
-    if ( dsi->__elfnote_section )
+    /* Fall back to looking for the special '__xen_guest' section. */
+    if ( dsi->__elfnote_section == NULL )
+    {
+        for ( h = 0; h < ehdr->e_shnum; h++ )
+        {
+            shdr = (Elf_Shdr *)(image + ehdr->e_shoff + (h*ehdr->e_shentsize));
+            if ( is_xen_guest_section(shdr, shstrtab) )
+            {
+                dsi->__xen_guest_string = (char *)image + shdr->sh_offset;
+                break;
+            }
+        }
+    }
+
+    /* Check the contents of the Xen notes or guest string. */
+    if ( dsi->__elfnote_section || dsi->__xen_guest_string )
     {
         const char *loader = xen_elfnote_string(dsi, XEN_ELFNOTE_LOADER);
         const char *guest_os = xen_elfnote_string(dsi, XEN_ELFNOTE_GUEST_OS);
         const char *xen_version =
             xen_elfnote_string(dsi, XEN_ELFNOTE_XEN_VERSION);
 
-        if ( ( loader == NULL || strcmp(loader, "generic") ) &&
-             ( guest_os == NULL || strcmp(guest_os, "linux") ) )
+        if ( ( loader == NULL || strncmp(loader, "generic", 7) ) &&
+             ( guest_os == NULL || strncmp(guest_os, "linux", 5) ) )
         {
             printk("ERROR: Will only load images built for the generic "
                    "loader or Linux images");
             return -EINVAL;
         }
 
-        if ( xen_version == NULL || strcmp(xen_version, "xen-3.0") )
+        if ( xen_version == NULL || strncmp(xen_version, "xen-3.0", 7) )
         {
             printk("ERROR: Xen will only load images built for Xen v3.0\n");
         }
     }
+    else
+    {
+#if defined(__x86_64__) || defined(__i386__)
+        printk("ERROR: Not a Xen-ELF image: "
+               "No ELF notes or '__xen_guest' section found.\n");
+        return -EINVAL;
+#endif
+    }
+
+    /*
+     * If we have ELF notes then PAE=yes implies that we must support
+     * the extended cr3 syntax. Otherwise we need to find the
+     * [extended-cr3] syntax in the __xen_guest string.
+     */
+    dsi->pae_kernel = PAEKERN_no;
+    if ( dsi->__elfnote_section )
+    {
+        p = xen_elfnote_string(dsi, XEN_ELFNOTE_PAE_MODE);
+        if ( p != NULL && strncmp(p, "yes", 3) == 0 )
+            dsi->pae_kernel = PAEKERN_extended_cr3;
+
+    }
+    else
+    {
+        p = xen_guest_lookup(dsi, XEN_ELFNOTE_PAE_MODE);
+        if ( p != NULL && strncmp(p, "yes", 3) == 0 )
+        {
+            dsi->pae_kernel = PAEKERN_yes;
+            if ( !strncmp(p+4, "[extended-cr3]", 14) )
+                dsi->pae_kernel = PAEKERN_extended_cr3;
+        }
+    }
 
     /* Initial guess for v_start is 0 if it is not explicitly defined. */
     dsi->v_start =
@@ -187,11 +312,24 @@ int parseelfimage(struct domain_setup_info *dsi)
     if ( !virt_base_defined )
         dsi->v_start = 0;
 
-    /* We are using the ELF notes interface so the default is 0. */
+    /*
+     * If we are using the legacy __xen_guest section then elf_pa_off
+     * defaults to v_start in order to maintain compatibility with
+     * older hypervisors which set padd in the ELF header to
+     * virt_base.
+     *
+     * If we are using the modern ELF notes interface then the default
+     * is 0.
+     */
     dsi->elf_paddr_offset =
         xen_elfnote_numeric(dsi, XEN_ELFNOTE_PADDR_OFFSET, &elf_pa_off_defined);
     if ( !elf_pa_off_defined )
-        dsi->elf_paddr_offset = 0;
+    {
+        if ( dsi->__elfnote_section )
+            dsi->elf_paddr_offset = 0;
+        else
+            dsi->elf_paddr_offset = dsi->v_start;
+    }
 
     if ( elf_pa_off_defined && !virt_base_defined )
     {
@@ -219,6 +357,7 @@ int parseelfimage(struct domain_setup_info *dsi)
     }
 
     dsi->v_kernentry = ehdr->e_entry;
+
     virt_entry =
         xen_elfnote_numeric(dsi, XEN_ELFNOTE_ENTRY, &virt_entry_defined);
     if ( virt_entry_defined )
@@ -234,7 +373,7 @@ int parseelfimage(struct domain_setup_info *dsi)
     }
 
     p = xen_elfnote_string(dsi, XEN_ELFNOTE_BSD_SYMTAB);
-    if ( p != NULL && strcmp(p, "yes") == 0 )
+    if ( p != NULL && strncmp(p, "yes", 3) == 0 )
         dsi->load_symtab = 1;
 
     dsi->v_kernstart = kernstart;
index e799b15764db7f2fe2aefdf4f2b86f3e6fae0920..b32f1616c6404c6f1eff1bcf00360c17b7571a85 100644 (file)
@@ -179,13 +179,24 @@ struct domain_setup_info
     unsigned long v_kernstart;
     unsigned long v_kernend;
     unsigned long v_kernentry;
+#define PAEKERN_no           0
+#define PAEKERN_yes          1
+#define PAEKERN_extended_cr3 2
+    unsigned int  pae_kernel;
     /* Initialised by loader: Private. */
     unsigned long elf_paddr_offset;
     unsigned int  load_symtab;
     unsigned long symtab_addr;
     unsigned long symtab_len;
-    /* Indicate whether it's xen specific image */
+    /*
+     * Only one of __elfnote_* or __xen_guest_string will be
+     * non-NULL.
+     *
+     * You should use the xen_elfnote_* accessors below in order to
+     * pickup the correct one and retain backwards compatibility.
+     */
     void *__elfnote_section, *__elfnote_section_end;
+    char *__xen_guest_string;
 };
 
 extern struct vcpu *idle_vcpu[NR_CPUS];